
const transparentPngUrl = '';

/**
 * A `RequestParameters` object to be returned from Map.options.transformRequest callbacks.
 * @typedef {Object} RequestParameters
 * @property {string} url The URL to be requested.
 * @property {Object} headers The headers to be sent with the request.
 * @property {string} credentials `'same-origin'|'include'` Use 'include' to send cookies with cross-origin requests.
 */
export interface RequestParameters {
  url: string;
  headers?: any;
  credentials?: 'same-origin' | 'include';
}

function makeRequest(requestParameters: RequestParameters): XMLHttpRequest {
  const xhr: XMLHttpRequest = new XMLHttpRequest();

  xhr.open('GET', requestParameters.url, true);
  for (const k in requestParameters.headers) {
      xhr.setRequestHeader(k, requestParameters.headers[k]);
  }
  xhr.withCredentials = requestParameters.credentials === 'include';
  return xhr;
}

export class Ajax {
  static getJSON(requestParameters: RequestParameters, callback: Function) {
    const xhr = makeRequest(requestParameters);
    xhr.setRequestHeader('Accept', 'application/json');
    xhr.onerror = () => {
      callback(new Error(xhr.statusText));
    };
    xhr.onload = () => {
      if (xhr.status >= 200 && xhr.status < 300 && xhr.response) {
        let data;
        try {
          data = JSON.parse(xhr.response);
        } catch (err) {
          return callback(err);
        }
        callback(null, data);
      } else {
        callback(new Error(`${xhr.status}: ${xhr.statusText}`));
      }
    };
    xhr.send();
    return xhr;
  }

  static getArrayBuffer(requestParameters: RequestParameters,
    callback: (error:Error|null, result?: {
      data: ArrayBuffer,
      cacheControl: string | null,
      expires: string | null}) => void) {
    const xhr = makeRequest(requestParameters);
    xhr.responseType = 'arraybuffer';
    xhr.onerror = function() {
      callback(new Error(xhr.statusText));
    };
    xhr.onload = function() {
      const response: ArrayBuffer = xhr.response;
      if (response.byteLength === 0 && xhr.status === 200) {
        return callback(new Error('http status 200 returned without content.'));
      }
      if (xhr.status >= 200 && xhr.status < 300 && xhr.response) {
        callback(null, {
          data: response,
          cacheControl: xhr.getResponseHeader('Cache-Control'),
          expires: xhr.getResponseHeader('Expires') || ""
        });
      } else {
        callback(new Error(`${xhr.status}: ${xhr.statusText}`));
      }
    };
    xhr.send();
    return xhr;
  }

  static getImage(requestParameters: RequestParameters, callback: (error:Error|null, elem?: HTMLImageElement) => void) {
    // request the image with XHR to work around caching issues
    // see https://github.com/mapbox/mapbox-gl-js/issues/1470
    return Ajax.getArrayBuffer(requestParameters, (err, imgData) => {
        if (err) {
            callback(err);
        } else if (imgData) {
            const img: HTMLImageElement = new Image();
            const URL = window.URL || (<any>window).webkitURL;
            img.onload = () => {
                callback(null, img);
                URL.revokeObjectURL(img.src);
            };
            const blob: Blob = new window.Blob([new Uint8Array(imgData.data)], { type: 'image/png' });
            (<any>img).cacheControl = imgData.cacheControl;
            (<any>img).expires = imgData.expires;
            img.src = imgData.data.byteLength ? URL.createObjectURL(blob) : transparentPngUrl;
        }
    });
  }
}